SPA 路由记忆

名词解释

在中后台系统开发中,访问任何页面时,认证是永远绕不过的槛。以登录为例,如果检测出当前用户未登录,会强制跳转到登录页面提示用户进行登录。登录完成后,系统需要跳转至用户原先想访问的页面。这个过程,暂且称之为路由记忆,前面例子中的登录页面称之为记忆节点

解决方案

由前面的例子我们不难得出路由记忆的关键逻辑:

  • 进入记忆节点时记录目标页面
  • 离开记忆节点时跳往目标页面(如果存在)
// 目标页面
const target = undefined;

但如此只能满足存在一个记忆节点的应用,而实际开发中,我们可能需要多个记忆节点。当应用中存在多个记忆节点时,问题就会慢慢的浮现。

接下来,我们对上面的方案进行扩展。

记忆节点集合

首先,我们需要维护一个记忆节点的集合:

// 记忆节点集合
const nodes = [];
上次访问记录

在这之前我们得先思考一个问题:由记忆节点跳往记忆节点时,是否需要触发路由记忆?

当然不需要!!!

当进入记忆节点时,我们首先得判断上一个访问页面是否为记忆节点,是则忽略,否则记录目标页面。

在这之前,我们需要定义一个变量用来记录上次访问页面,这个变量仅记录上次访问页面即可,无论是否为记忆节点:

// 上次访问页面
const previous = undefined;
记录目标页面

为保证路由记忆结果的质量,我们需要对每次路由跳转结束进行监控(不论成功、取消或者失败)。

结合前面的例子,我们现在不难得出,只有在常规页面进入记忆节点时,需要对目标页面进行记录。

/**
 * @param to {string} 去往页面
 */
function mark (to) {
    // 备份上次访问页面,防止被覆写
    const backup = previous;
    // 记录上次访问页面
    previous = to;
    // 如果上一个访问页面是记忆节点,中断函数
    if (~nodes.indexOf(backup)) return;
    // 如果去往页面不是记忆节点,中断函数
    if (!~nodes.indexOf(to)) return;
    // 记录目标页面
    target = backup;
}
跳往目标页面

在每次路由跳转之前,我们需要检测是否是记忆节点进入常规页面且是否存在目标页面记录,如果条件都满足,则打断原有操作跳往目标页面,并清空目标页面记录。

/**
 * @param to {string} 去往页面
 */
function check (to) {
    // 若目标页面不存在,中断函数
    if (!target) return;
    // 备份目标页面,防止在使用之前被清空
    const backup = target;
    // 如果去往页面是记忆节点,中断函数
    if (~nodes.indexOf(to)) return;
    // 如果上次访问不是记忆节点,中断函数
    if (!~nodes.indexOf(previous)) return;
    // 清空目标记录
    target = undefined;
    // 路由跳转,假定执行函数为 navigate
    navigate(target);
}

代码整合

ES5
var RouterMemory = function (nodes) {
    // 目标页面
    this.target = undefined;
    // 上次访问页面
    this.previous = undefined;
    // 记忆节点集合
    this.nodes = nodes && nodes instanceof Array ? nodes : [];
}

/**
 * 记录上次访问及目标页面
 * @param to {string} 去往页面
 */
RouterMemory.prototype.mark = function (to) {
    // 备份上次访问页面,防止被覆写
    var backup = this.previous;
    // 记录上次访问页面
    this.previous = to;
    // 如果上一个访问页面是记忆节点,中断函数
    if (~nodes.indexOf(backup)) return;
    // 如果去往页面不是记忆节点,中断函数
    if (!~nodes.indexOf(to)) return;
    // 记录目标页面
    this.target = backup;
}

/**
 * 跳往目标页面
 * @param to {string} 去往页面
 */
RouterMemory.prototype.check = function (to) {
    // 若目标页面不存在,中断函数
    if (!this.target) return;
    // 备份目标页面,防止在使用之前被清空
    const backup = this.target;
    // 如果去往页面是记忆节点,中断函数
    if (~nodes.indexOf(to)) return;
    // 如果上次访问不是记忆节点,中断函数
    if (!~nodes.indexOf(this.previous)) return;
    // 清空目标页面
    this.target = undefined;
    // 路由跳转,假定执行函数为 navigate
    navigate(this.target);
}
ES Next
class RouterMemory {
    constructor (nodes) {
        // 目标页面
        this.target = undefined;
        // 上次访问页面
        this.previous = undefined;
        // 记忆节点集合
        this.nodes = nodes && nodes instanceof Array ? nodes : [];
    }
    /**
    * @param to {string} 去往页面
    */
    mark (to) {
        // 备份上次访问页面,防止被覆写
        const backup = this.previous;
        // 记录上次访问页面
        this.previous = to;
        // 如果上一个访问页面是记忆节点,中断函数
        if (nodes.includes(backup)) return;
        // 如果去往页面不是记忆节点,中断函数
        if (!nodes.includes(to)) return;
        // 记录目标页面
        this.target = backup;
    }
    /**
    * @param to {string} 去往页面
    */
    check (to) {
        // 若目标页面不存在,中断函数
        if (!this.target) return;
        // 备份目标页面,防止在使用之前被清空
        const backup = this.target;
        // 如果去往页面是记忆节点,中断函数
        if (nodes.includes(to)) return;
        // 如果上次访问不是记忆节点,中断函数
        if (!nodes.includes(this.previous)) return;
        // 清空目标记录
        this.target = undefined;
        // 路由跳转,假定执行函数为 navigate
        navigate(this.target);
    }
}

xunjianxiang
6 声望0 粉丝